<template>
  <div class="container">
    <div ref="canvasContainer" class="canvas-container"></div>
    <div class="info-panel">
      <h3>Three.js 射线检测示例</h3>
      <p class="tip">点击场景中的立方体进行选择</p>
      
      <div class="selected-info" v-if="selectedObject">
        <h4>选中的对象信息</h4>
        <p><strong>名称:</strong> {{ selectedObject.name }}</p>
        <p><strong>位置:</strong> 
          ({{ selectedObject.position.x.toFixed(2) }}, 
           {{ selectedObject.position.y.toFixed(2) }}, 
           {{ selectedObject.position.z.toFixed(2) }})
        </p>
        <p v-if="intersectInfo">
          <strong>相交点:</strong> 
          ({{ intersectInfo.point.x.toFixed(2) }}, 
           {{ intersectInfo.point.y.toFixed(2) }}, 
           {{ intersectInfo.point.z.toFixed(2) }})
        </p>
        <p v-if="intersectInfo">
          <strong>距离:</strong> {{ intersectInfo.distance.toFixed(2) }}
        </p>
      </div>
      
      <div v-else class="no-selection">
        <p>未选择任何对象</p>
        <p class="hint">点击任意立方体查看详细信息</p>
      </div>
      
      <div class="controls">
        <button @click="resetSelection" class="reset-btn">重置选择</button>
        <button @click="addRandomCube" class="add-btn">添加随机立方体</button>
      </div>
      
      <div class="stats">
        <p><strong>场景统计:</strong></p>
        <p>立方体数量: {{ cubeCount }}</p>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'

// DOM 引用
const canvasContainer = ref(null)

// 响应式数据
const selectedObject = ref(null)
const intersectInfo = ref(null)
const cubes = ref([]) // 存储所有立方体，方便管理

// Three.js 相关变量
let scene, camera, renderer, controls, raycaster, mouse, labelRenderer
let currentLabel = null // 当前显示的信息标签

// 计算属性
const cubeCount = computed(() => cubes.value.length)

// 获取颜色名称
const getColorName = (hexColor) => {
  const colorMap = {
    0xff0000: '红色',
    0x00ff00: '绿色',
    0x0000ff: '蓝色',
    0xffff00: '黄色',
    0xff00ff: '紫色',
    0x00ffff: '青色',
    0xffffff: '白色',
    0x000000: '黑色'
  }
  return colorMap[hexColor] || `#${hexColor.toString(16).padStart(6, '0')}`
}

// 创建信息标签
const createInfoLabel = (cube) => {
  // 移除旧的标签
  removeInfoLabel()

  // 创建标签元素
  const labelDiv = document.createElement('div')
  labelDiv.className = 'cube-label'
  labelDiv.innerHTML = `
    <div class="label-content">
      <div class="label-title">${cube.name}</div>
      <div class="label-info">颜色: ${getColorName(cube.userData.originalColor)}</div>
    </div>
  `

  // 创建CSS2DObject
  const label = new CSS2DObject(labelDiv)
  // 将标签定位在立方体上方
  label.position.set(0, 1.2, 0) // Y轴向上偏移1.2个单位
  cube.add(label) // 将标签添加到立方体上，这样会跟随立方体移动
  
  currentLabel = label
  return label
}

// 移除信息标签
const removeInfoLabel = () => {
  if (currentLabel) {
    const parent = currentLabel.parent
    if (parent) {
      parent.remove(currentLabel)
    }
    if (currentLabel.element && currentLabel.element.parentNode) {
      currentLabel.element.parentNode.removeChild(currentLabel.element)
    }
    currentLabel = null
  }
}

// 获取画布尺寸
const getCanvasSize = () => {
  if (!canvasContainer.value) {
    return {
      width: window.innerWidth,
      height: window.innerHeight
    }
  }
  const rect = canvasContainer.value.getBoundingClientRect()
  return {
    width: rect.width,
    height: rect.height
  }
}

// 初始化场景
const initScene = () => {
  // 创建场景
  scene = new THREE.Scene()
  scene.background = new THREE.Color(0xf0f0f0)

  const { width, height } = getCanvasSize()

  // 创建相机
  camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)
  camera.position.set(5, 5, 5)
  camera.lookAt(0, 0, 0)

  // 创建渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true })
  renderer.setSize(width, height)
  renderer.shadowMap.enabled = true
  renderer.shadowMap.type = THREE.PCFSoftShadowMap
  canvasContainer.value.appendChild(renderer.domElement)

  // 创建CSS2D渲染器用于显示标签
  labelRenderer = new CSS2DRenderer()
  labelRenderer.setSize(width, height)
  labelRenderer.domElement.style.position = 'absolute'
  labelRenderer.domElement.style.top = '0'
  labelRenderer.domElement.style.left = '0'
  labelRenderer.domElement.style.pointerEvents = 'none'
  canvasContainer.value.appendChild(labelRenderer.domElement)

  // 添加轨道控制器
  controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true
  controls.dampingFactor = 0.05

  // 初始化射线检测器
  raycaster = new THREE.Raycaster()
  mouse = new THREE.Vector2()

  // 添加光源
  addLights()
  
  // 添加物体
  addObjects()

  // 添加坐标轴辅助
  const axesHelper = new THREE.AxesHelper(5)
  scene.add(axesHelper)

  // 添加网格辅助
  const gridHelper = new THREE.GridHelper(20, 20)
  scene.add(gridHelper)
}

// 添加光源
const addLights = () => {
  // 环境光
  const ambientLight = new THREE.AmbientLight(0x404040, 0.6)
  scene.add(ambientLight)

  // 方向光
  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8)
  directionalLight.position.set(10, 10, 5)
  directionalLight.castShadow = true
  directionalLight.shadow.mapSize.width = 2048
  directionalLight.shadow.mapSize.height = 2048
  scene.add(directionalLight)
}

// 添加测试物体
const addObjects = () => {
  const colors = [
    0xff0000, // 红
    0x00ff00, // 绿
    0x0000ff, // 蓝
    0xffff00, // 黄
    0xff00ff, // 紫
    0x00ffff, // 青
  ]

  // 创建多个立方体
  for (let i = 0; i < 5; i++) {
    const cube = createCube(colors[i % colors.length], i)
    cubes.value.push(cube)
    scene.add(cube)
  }

  // 添加一个平面作为地面
  const planeGeometry = new THREE.PlaneGeometry(20, 20)
  const planeMaterial = new THREE.MeshStandardMaterial({ 
    color: 0xcccccc,
    side: THREE.DoubleSide 
  })
  const plane = new THREE.Mesh(planeGeometry, planeMaterial)
  plane.rotation.x = -Math.PI / 2
  plane.position.y = -5
  plane.receiveShadow = true
  plane.name = '地面'
  scene.add(plane)
}

// 创建立方体
const createCube = (color, index) => {
  const geometry = new THREE.BoxGeometry(1, 1, 1)
  const material = new THREE.MeshStandardMaterial({ color })
  const cube = new THREE.Mesh(geometry, material)
  
  // 使用更规律的分布，避免重叠
  // 将立方体排列在一个圆形或网格上，确保每个都能被点击到
  const angle = (index / 5) * Math.PI * 2
  const radius = 3
  cube.position.x = Math.cos(angle) * radius
  cube.position.y = (Math.random() - 0.5) * 2 // Y轴稍微随机
  cube.position.z = Math.sin(angle) * radius
  
  cube.name = `立方体 ${index + 1}`
  cube.userData = { originalColor: color }
  cube.castShadow = true
  
  // 确保立方体可以被射线检测到
  cube.raycast = THREE.Mesh.prototype.raycast
  
  return cube
}

// 添加随机立方体
const addRandomCube = () => {
  const colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff]
  const randomColor = colors[Math.floor(Math.random() * colors.length)]
  const index = cubes.value.length
  const cube = createCube(randomColor, index)
  
  // 为新立方体设置一个随机但不会重叠的位置
  const angle = Math.random() * Math.PI * 2
  const radius = 3 + Math.random() * 2 // 稍微远一点
  cube.position.x = Math.cos(angle) * radius
  cube.position.y = (Math.random() - 0.5) * 2
  cube.position.z = Math.sin(angle) * radius
  
  cubes.value.push(cube)
  scene.add(cube)
  console.log(`添加了新立方体: ${cube.name}，位置:`, cube.position)
}

// 处理鼠标点击事件
const onMouseClick = (event) => {
  // 计算鼠标在归一化设备坐标中的位置 (-1 到 +1)
  const rect = renderer.domElement.getBoundingClientRect()
  mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1
  mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1

  // 更新射线的起点和方向
  // Raycaster 会从相机位置向鼠标点击的方向发射一条射线
  raycaster.setFromCamera(mouse, camera)

  // 只检测立方体，不检测辅助对象和地面
  // 这样可以避免辅助对象（坐标轴、网格）干扰检测
  const objectsToTest = cubes.value.filter(cube => cube.name && cube.name.startsWith('立方体'))
  const intersects = raycaster.intersectObjects(objectsToTest, false)
  console.log('intersects==', intersects)
  console.log('检测到的相交对象数量:', intersects.length)
  if (intersects.length > 0) {
    console.log('所有相交对象:', intersects.map(i => i.object.name))
  }

  // 重置之前选中的物体颜色和标签
  if (selectedObject.value) {
    const originalColor = selectedObject.value.userData?.originalColor
    if (originalColor !== undefined) {
      selectedObject.value.material.color.setHex(originalColor)
    }
    removeInfoLabel()
    selectedObject.value = null
    intersectInfo.value = null
  }

  // 如果有相交的物体，选择第一个（最近的）
  if (intersects.length > 0) {
    const intersect = intersects[0]
    const object = intersect.object

    // 高亮选中的物体
    object.material.color.set(0xffffff) // 设置为白色高亮
    selectedObject.value = object
    intersectInfo.value = {
      point: intersect.point,
      distance: intersect.distance
    }
    console.log('object==', object)
    // 在立方体上方显示信息标签
    createInfoLabel(object)

    console.log('✅ 选中对象:', object.name)
    console.log('相交点:', intersect.point)
    console.log('距离:', intersect.distance)
  } else {
    console.log('❌ 未检测到任何立方体，可能点击了空白区域或被其他物体遮挡')
  }
}

// 重置选择
const resetSelection = () => {
  if (!selectedObject.value) return
  
  const originalColor = selectedObject.value.userData?.originalColor
  if (originalColor !== undefined) {
    selectedObject.value.material.color.setHex(originalColor)
  }
  
  removeInfoLabel()
  selectedObject.value = null
  intersectInfo.value = null
}

// 动画循环
const animate = () => {
  requestAnimationFrame(animate)
  controls.update()
  renderer.render(scene, camera)
  // 更新CSS2D渲染器
  if (labelRenderer) {
    labelRenderer.render(scene, camera)
  }
}

// 窗口大小调整
const onWindowResize = () => {
  const { width, height } = getCanvasSize()
  camera.aspect = width / height
  camera.updateProjectionMatrix()
  renderer.setSize(width, height)
  if (labelRenderer) {
    labelRenderer.setSize(width, height)
  }
}

// 组件挂载
onMounted(() => {
  initScene()
  animate()

  // 添加事件监听
  renderer.domElement.addEventListener('click', onMouseClick)
  window.addEventListener('resize', onWindowResize)
})

// 组件卸载
onUnmounted(() => {
  if (renderer) {
    renderer.domElement.removeEventListener('click', onMouseClick)
    window.removeEventListener('resize', onWindowResize)
    
    // 清理资源
    renderer.dispose()
    if (controls) controls.dispose()
  }
})
</script>

<style scoped>
.container {
  display: flex;
  height: 70vh;
  width: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
}

.canvas-container {
  flex: 1;
  height: 100%;
  position: relative;
}

.info-panel {
  width: 320px;
  padding: 20px;
  background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
  color: white;
  display: flex;
  flex-direction: column;
  gap: 15px;
  overflow-y: auto;
  box-shadow: -2px 0 10px rgba(0, 0, 0, 0.3);
}

.info-panel h3 {
  margin: 0 0 10px 0;
  color: #3498db;
  font-size: 20px;
  border-bottom: 2px solid #3498db;
  padding-bottom: 10px;
}

.info-panel h4 {
  margin: 0 0 10px 0;
  color: #3498db;
  font-size: 16px;
}

.tip {
  background: rgba(52, 152, 219, 0.2);
  padding: 10px;
  border-radius: 5px;
  border-left: 3px solid #3498db;
  font-size: 14px;
  margin: 0;
}

.selected-info {
  background: #34495e;
  padding: 15px;
  border-radius: 8px;
  border-left: 4px solid #3498db;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}

.selected-info p {
  margin: 8px 0;
  font-size: 14px;
  line-height: 1.6;
}

.selected-info strong {
  color: #3498db;
}

.no-selection {
  background: #34495e;
  padding: 20px;
  border-radius: 8px;
  border-left: 4px solid #e74c3c;
  text-align: center;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}

.no-selection p {
  margin: 5px 0;
}

.hint {
  font-size: 12px;
  color: #95a5a6;
  font-style: italic;
}

.controls {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.reset-btn,
.add-btn {
  padding: 12px 20px;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 14px;
  font-weight: bold;
  transition: all 0.3s;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}

.reset-btn {
  background: #e74c3c;
}

.reset-btn:hover {
  background: #c0392b;
  transform: translateY(-2px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}

.add-btn {
  background: #27ae60;
}

.add-btn:hover {
  background: #229954;
  transform: translateY(-2px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}

.stats {
  background: rgba(52, 73, 94, 0.5);
  padding: 15px;
  border-radius: 5px;
  margin-top: auto;
}

.stats p {
  margin: 5px 0;
  font-size: 14px;
}

.stats strong {
  color: #3498db;
}

p {
  margin: 5px 0;
}
</style>

<style>
/* 立方体信息标签样式 - 全局样式，因为CSS2DObject创建的元素在组件外部 */
.cube-label {
  pointer-events: none;
  user-select: none;
}

.label-content {
  background: rgba(0, 150, 255, 0.85);
  backdrop-filter: blur(10px);
  border-radius: 8px;
  padding: 12px 16px;
  min-width: 150px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3),
              0 0 20px rgba(0, 150, 255, 0.4);
  border: 1px solid rgba(255, 255, 255, 0.2);
  color: white;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  text-align: center;
  transform: translateX(-50%);
}

.label-title {
  font-size: 16px;
  font-weight: bold;
  margin-bottom: 6px;
  color: #ffffff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}

.label-info {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.95);
  line-height: 1.4;
}
</style>